/*
 * @(#)JaccardFunction.java
 *
 * Revision:
 * Author                                         Date           
 * --------------------------------------------   ------------   
 * Ana Emília Victor Barbosa Coutinho             25/06/2012    
 */
package br.edu.ufcg.splab.techniques.reduction.functions.similarity;


import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.generator.TestCase;


public class JaccardIndex implements DistanceFunction{

	private final double W = 1.0;

	/**
	 * Jaccard index (similarity).
	 * 
	 * @param path1 first test case.
	 * @param path2 second test case.
	 * @return similarity's value between the two paths. 
	 */
	public double getSimilarity(TestCase path1, TestCase path2){
		return getSimilarity(path1, path2, W);
	}
	
	/**
     * Provide a string representation of the similarity function to be written.
     * @return The string representation of the similarity function.
     */
	public String getFunctionName(){
		return "Jac";
	}
	
	/*
	 * This family of measures is defined based on commonalities and differences between two sets of inputs.
	 * 
	 * sim(A,B) = |A intersection B|/|A intersection B| + w * (|A union B| - |A intersection B|)
	 * 
	 * Jaccard's index, proposed by [Jaccard 1901], is a similarity measure between sample sets.
	 * This measure of similarity is defined by the following formula:
	 * J(A,B) = |A intersection B|/|A union B|, i.e., when w = 1, that is the size of the intersection 
	 * divided by the size of the union of the sample sets.
	 * 
	 * Jaccard, P. (1901). Étude comparative de la distribution florale dans
	 * une portion des alpes et des jura. Bulletin de la Société Vaudoise des Sciences
	 * Naturelles 37, pages 547-579.
	 */
	protected double getSimilarity(TestCase path1, TestCase path2, double w) {
		
		if(path1.equals(path2)){
			return 0;
		}
		
		Map<String, Integer> mapTC1 = new HashMap<String, Integer>();
		for (InterfaceEdge edge:path1.getTestCase()) {		
			if(mapTC1.containsKey(edge.getLabel())){
				int cont = mapTC1.get(edge.getLabel());
				mapTC1.put(edge.getLabel(), cont + 1);
			}else{
				mapTC1.put(edge.getLabel(), 1);
			}
		}

		Map<String, Integer> mapTC2 = new HashMap<String, Integer>();
		for (InterfaceEdge edge:path2.getTestCase()) {
			if(mapTC2.containsKey(edge.getLabel())){
				int cont = mapTC2.get(edge.getLabel());
				mapTC2.put(edge.getLabel(), cont + 1);
			}else{
				mapTC2.put(edge.getLabel(), 1);
			}			
		}		
		
		int set_identical = 0;
		Set<String> set_u = new HashSet<String>();
		int set_union = 0;
		
		Set<Entry<String, Integer>> set = mapTC1.entrySet();
		for(Entry<String, Integer> entr : set){
			set_u.add(entr.getKey());
			if(mapTC2.containsKey(entr.getKey())){
				set_identical = set_identical + 1;
			}
		}
		
		set = mapTC2.entrySet();
		for(Entry<String, Integer> entr : set){
			set_u.add(entr.getKey());
		}
				
		double totalSimilarity =  ((double) set_identical / (set_identical + w * (set_union - set_identical)));
		return totalSimilarity;
	}
	
	
}
